﻿using LightCAD.Core.Element3d;
using LightCAD.MathLib;
using System.Collections.Generic;
using static System.Windows.Forms.DataFormats;

namespace QdArch
{
    public class StairAttribute : CptAttribute
    {
        public StairAttribute(string category, string subCategorys) : base(category, subCategorys)
        {
        }
        public const string BuiltinUuid = "{DCFE29E5-3320-4FDA-A827-6AA288230A78}";

        public override LcComponentDefinition GetBuiltinCpt(string subCategory)
        {
            return new QdStair(BuiltinUuid,"内置","双跑楼梯");
        }
    }
    [StairAttribute("楼梯", "双跑楼梯")]
    public class QdStair : LcComponentDefinition
    {
        public QdStair()
        {
            this.TypeParameters = new LcParameterSet(CptTypeParamsDef<QdStair>.ParamsDef);
        }
        static QdStair()
        {
            CptTypeParamsDef<QdStair>.ParamsDef = new ParameterSetDefinition { new ParameterDefinition { DataType = LcDataType.String, Name = "MaterialId", DisplayName = "材质", } };
        }
        public QdStair(string uuid, string name, string subCategory) : base(uuid, name, "楼梯", subCategory, new LcParameterSet(CptTypeParamsDef<QdStair>.ParamsDef))
        {
            if (subCategory =="双跑楼梯")
            {
                this.Parameters = new ParameterSetDefinition()
            {
                     new ParameterDefinition
                     {
                         Name = "StepHeight",
                         DataType= LcDataType.Double,
                     },
                     new ParameterDefinition
                     {
                         Name = "StepWidth",
                          DataType= LcDataType.Double,
                     },
                    new ParameterDefinition
                     {
                         Name = "StepNum",
                          DataType= LcDataType.Int,
                     },
                    new ParameterDefinition
                     {
                         Name = "LadderWidth",
                          DataType= LcDataType.Double,
                     },
                     new ParameterDefinition
                     {
                         Name = "PlatWidth",
                         DataType= LcDataType.Double,
                     },
                    new ParameterDefinition
                     {
                         Name = "PlatLen",
                         DataType= LcDataType.Double,
                     },
                     new ParameterDefinition
                     {
                         Name = "PlatThickness",
                         DataType= LcDataType.Double,
                     },
                     new ParameterDefinition
                     {
                         Name = "SlabThickness",
                         DataType= LcDataType.Double,
                     },
                       new ParameterDefinition
                     {
                         Name = "StepThickness",
                         DataType= LcDataType.Double,
                     },
                     new ParameterDefinition
                     {
                         Name = "RiserThickness",
                         DataType= LcDataType.Double,
                     },
            };
                this.Curve2dProvider = new Curve2dProvider("楼梯")
                {
                    GetCurve2dsFunc = (pset) =>
                    {
                        var curves = new List<Curve2d>();
                        var stepwidth = pset.GetValue<double>("StepWidth");
                        var stepheight = pset.GetValue<double>("StepHeight");
                        var stepnum = pset.GetValue<int>("StepNum");
                        var ladderwidth = pset.GetValue<double>("LadderWidth");
                        var platwidth = pset.GetValue<double>("PlatWidth");
                        var platlen = pset.GetValue<double>("PlatLen");
                        for (int i = 0; i < stepnum; i++)
                        {
                            var rect1 = new Rect2d();
                            rect1.Min = new Vector2(0, i * stepwidth);
                            rect1.Max = new Vector2(ladderwidth, (i + 1) * stepwidth);
                            var rect2 = new Rect2d();
                            rect2.Min = new Vector2(platlen - ladderwidth, (stepnum - i - 1) * stepwidth);
                            rect2.Max = new Vector2(platlen, (stepnum - i) * stepwidth);
                            curves.Add(rect1);
                            curves.Add(rect2);
                        }
                        var rectEnd = new Rect2d();
                        rectEnd.Min = new Vector2(0, stepnum * stepwidth);
                        rectEnd.Max = new Vector2(platlen, stepnum * stepwidth + platwidth);
                        //var rectEnd2 = new Rect2d();
                        //rectEnd2.Min = new Vector2(0, -ladderwidth);
                        //rectEnd2.Max = new Vector2(platlen, 0);
                        curves.Add(rectEnd);
                        //curves.Add(rectEnd2);
                        return new Curve2dGroupCollection() { new Curve2dGroup()
                        {
                            Curve2ds=curves.ToListEx()
                        } };

                    }
                };
                var mat = MaterialManager.GetMaterial(MaterialManager.ConcreteUuid);
                var frameMat = MaterialManager.GetMaterial(MaterialManager.WoodUuid);
                var ptxMat = MaterialManager.GetMaterial(MaterialManager.PtxUuid);

                this.Solid3dProvider = new Solid3dProvider("楼梯")
                {
                    AssignMaterialsFunc = (c, s) => {
                        return new LcMaterial[] { frameMat, mat, ptxMat };
                    }
                };
                //this.Solid3dProvider = new Solid3dProvider("楼梯")
                //{
                //    GetSolid3dsFunc = (p) =>
                //    {
                //        var stepHeight = p.GetValue<double>("StepHeight");
                //        var stepWidth = p.GetValue<double>("StepWidth");
                //        var stepNum = p.GetValue<int>("StepNum");
                //        var ladderWidth = p.GetValue<double>("LadderWidth");
                //        var platWidth = p.GetValue<double>("PlatWidth");
                //        var platLen = p.GetValue<double>("PlatLen");
                //        var platThickness = p.GetValue<double>("PlatThickness");
                //        var slabThickness = p.GetValue<double>("SlabThickness");
                //        return new Solid3dCollection { new Stair3d(stepHeight, stepWidth, stepNum, ladderWidth, platWidth, platLen, platThickness, slabThickness) };
                //    }
                //};
            }
        }
    }
    public class QdStairInstance : LcComponentInstance
    {
        //public object Rt3DAction { get; set; } = StairAction3D.Instance;
        //public LcParameterSet Parameters { get; set; }
        public double StepHeight { get => Parameters.GetValue<double>("StepHeight"); set => Set("StepHeight", value); }
        public double StepWidth { get => Parameters.GetValue<double>("StepWidth"); set => Set("StepWidth", value); }
        public int StepNum { get => Parameters.GetValue<int>("StepNum"); set => Set("StepNum", value); }
        public double LadderWidth { get => Parameters.GetValue<double>("LadderWidth"); set => Set("LadderWidth", value); }
        public double PlatWidth { get => Parameters.GetValue<double>("PlatWidth"); set => Set("PlatWidth", value); }
        public double PlatLen { get => Parameters.GetValue<double>("PlatLen"); set => Set("PlatLen", value); }
        public double PlatThickness { get => Parameters.GetValue<double>("PlatThickness"); set => Set("PlatThickness", value); }
        public double SlabThickness { get => Parameters.GetValue<double>("SlabThickness"); set => Set("SlabThickness", value); }
        public double StepThickness { get => Parameters.GetValue<double>("StepThickness"); set => Set("StepThickness", value); }
        public double RiserThickness { get => Parameters.GetValue<double>("RiserThickness"); set => Set("RiserThickness", value); }
        public QdStairInstance(QdStair stair) : base(stair)
        {
            this.Type = ArchElementType.Stair;
        }
 
        public QdStairInstance(double StepHeight, double StepWidth, int StepNum, double LadderWidth, double PlatWidth, double PlatLen, double PlatThickness, double SlabThickness,double StepThickness,double RiserThickness, QdStair stair) : this(stair)
        {
            this.Type = ArchElementType.Stair;
            this.Parameters.SetValue("StepHeight", StepHeight);
            this.Parameters.SetValue("StepWidth", StepWidth);
            this.Parameters.SetValue("StepNum", StepNum);
            this.Parameters.SetValue("LadderWidth", LadderWidth);
            this.Parameters.SetValue("PlatWidth", PlatWidth);
            this.Parameters.SetValue("PlatLen", PlatLen);
            this.Parameters.SetValue("PlatThickness", PlatThickness);
            this.Parameters.SetValue("SlabThickness", SlabThickness);
            this.Parameters.SetValue("StepThickness", StepThickness);
            this.Parameters.SetValue("RiserThickness", RiserThickness);
        }

        public override bool IntersectWithBox(Polygon2d testPoly, List<RefChildElement> intersectChildren = null)
        {
            Box2 thisBox = this.BoundingBox;
            if (!thisBox.IntersectsBox(testPoly.BoundingBox))
            {
                return false;
            }
            return true;
        }
        public override bool IncludedByBox(Polygon2d testPoly, List<RefChildElement> includedChildren = null)
        {
            Box2 thisBox = this.BoundingBox;
            if (!testPoly.BoundingBox.ContainsBox(thisBox))
            {
                return false;
            }
            return true;
        }
        public void Set(string name, object val)
        {
            OnPropertyChangedBefore(name, this.Parameters[name], val);
            this.Parameters.SetValue(name, val);
            OnPropertyChangedAfter(name, this.Parameters[name], val);
        }
        public override LcElement Clone()
        {
            var clone = new QdStairInstance(this.Definition as QdStair);
            clone.Copy(this);
            clone.Initilize(this.Document);
            return clone;
        }
        public override Box2 GetBoundingBox()
        {
            if (this.Parameters == null || this.Parameters.Values.Length == 0)
            {
                return Box2.Empty;
            }
            var width = this.StepNum * this.StepWidth + this.PlatWidth;
            var len = this.PlatLen;
            var points = new Vector2[] { new Vector2(), new Vector2(len, 0), new Vector2(len, width), new Vector2(0, width) };
            points = points.Select(n=>n.Add(this.Position.ToVector2())).ToArray();
            var box2d = new Box2().MakeEmpty();
            box2d = box2d.ExpandByPoints(points);
            return box2d;

        }
        public override Box2 GetBoundingBox(Matrix3 matrix)
        {
            if (this.Parameters == null || this.Parameters.Values.Length == 0)
            {
                return Box2.Empty;
            }
            var width = this.StepNum * this.StepWidth + this.PlatWidth;
            var len = this.PlatLen;
            var points = new Vector2[] { new Vector2(), new Vector2(len, 0), new Vector2(len, width), new Vector2(0, width) };
            points = points.Select(n => n.Add(this.Position.ToVector2())).ToArray();
            for (int i = 0; i < 4; i++)
            {
                points[i] = matrix.MultiplyPoint(points[i]);
            }
            var box2d = new Box2().MakeEmpty();
            box2d = box2d.ExpandByPoints(points);
            return box2d;

        }
        public override void Copy(LcElement src)
        {
            base.Copy(src);
            var stairRef = (QdStairInstance)src;
            this.Parameters = stairRef.Parameters.Definition.New();
            this.Definition = stairRef.Definition;
            var count = stairRef.Parameters.Values.Count();
            for (int i = 0; i < count; i++)
            {
                this.Parameters[i] = stairRef.Parameters[i];
            }

        }

        public override void WriteProperties(Utf8JsonWriter writer, JsonSerializerOptions soptions)
        {
            base.WriteProperties(writer, soptions);

            writer.WriteString("StairId", this.Definition.Uuid);
            writer.WriteNumberProperty(nameof(StepHeight), this.StepHeight);
            writer.WriteNumberProperty(nameof(StepWidth), this.StepWidth);
            writer.WriteNumberProperty(nameof(StepNum), this.StepNum);
            writer.WriteNumberProperty(nameof(LadderWidth), this.LadderWidth);
            writer.WriteNumberProperty(nameof(PlatWidth), this.PlatWidth);
            writer.WriteNumberProperty(nameof(PlatLen), this.PlatLen);
            writer.WriteNumberProperty(nameof(PlatThickness), this.PlatThickness);
            writer.WriteNumberProperty(nameof(SlabThickness), this.SlabThickness);
        }

        public override void ReadProperties(ref JsonElement jele)
        {
            base.ReadProperties(ref jele);

            var stairId = jele.ReadStringProperty("StairId");
            //var cpt = this.Document.Components[stairId];
            //var stair = new QdStair(cpt.SubCategory, cpt.Name);
            //stair.Uuid = stairId;
            var stair = ComponentManager.GetCptDef<QdStair>("楼梯", "楼梯", stairId);
            this.Definition = stair;
            //this.Document.Components.Remove(stairId);
            //this.Document.Components.Add(stair);             
            //this.Parameters = QdStair.ParamsDefinition.New();
            this.StepHeight = jele.ReadDoubleProperty(nameof(this.StepHeight));
            this.StepWidth = jele.ReadDoubleProperty(nameof(this.StepWidth));
            this.StepNum = jele.ReadIntProperty(nameof(this.StepNum));
            this.LadderWidth = jele.ReadDoubleProperty(nameof(this.LadderWidth));
            this.PlatWidth = jele.ReadDoubleProperty(nameof(this.PlatWidth));
            this.PlatLen = jele.ReadDoubleProperty(nameof(this.PlatLen));
            this.PlatThickness = jele.ReadDoubleProperty(nameof(this.PlatThickness));
            this.SlabThickness = jele.ReadDoubleProperty(nameof(this.SlabThickness));
        }
    }
}
